using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using TheLogWatcher.Properties;

namespace TheLogWatcher
{
    public partial class ProgressWindow : Form
    {
        public ProgressWindow(DataAccess dataAccess)
        {
            InitializeComponent();
            this.dataAccess = dataAccess;
        }

        /// <summary>
        ///     Returns true if everything went fine and
        ///     false if Cancel button was pressed or an error occured.
        /// </summary>
        /// <returns></returns>
        public bool RunFileIndexer()
        {
            // Set window title for this session
            this.Text = "Create log file indices";
            this.threadFileIndexer.RunWorkerAsync();
            DialogResult result = this.ShowDialog();
            if (result == DialogResult.Cancel || this.cancelled)
            {
                return false;
            }
            return true;
        }

        private DataAccess dataAccess;
        private bool cancelled;

        public void StopProcess()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(StopProcess));
            }
            else
            {
                if (this.threadFileIndexer.IsBusy)
                {
                    this.threadFileIndexer.CancelAsync();
                }
                this.cancelled = true;
                this.Close();
            }
        }

        private void ChangeProgressDescription(string description)
        {
            this.description = description;
            this.ChangeProgressDescription();
        }

        string description;

        private void ChangeProgressDescription()
        {
            if (this.IsDisposed)
            {
                return;
            }
            if (this.lblDescription.InvokeRequired)
            {
                this.lblDescription.Invoke(new MethodInvoker(ChangeProgressDescription));
            }
            else
            {
                this.lblDescription.Text = this.description;
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.StopProcess();
        }

        private void threadFileIndexer_DoWork(object sender, DoWorkEventArgs e)
        {
            this.dataAccess.FnProvider.ResetFileIterator();
            LogFile logfile = this.dataAccess.FnProvider.GetNextFile();
            while (logfile != null)
            {
                FileStream fileStream = new FileStream(logfile.Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                {
                    if (fileStream == null || fileStream.Length == 0 || this.IsDisposed)
                    {
                        this.StopProcess();
                    }
                    // Start at last position (needed for watcher mode)
                    long newPos = fileStream.Seek(logfile.LastKnownPosition, SeekOrigin.Begin);
                    Console.WriteLine("threadFileIndexer_DoWork: New position: " + newPos);
                    //
                    DateTime startTime = DateTime.Now;
                    long pieceSize = (fileStream.Length - logfile.LastKnownPosition) / 100;
                    Console.WriteLine("threadFileIndexer_DoWork: Piece size: " + pieceSize);
                    if (pieceSize == 0)
                    {
                        pieceSize = 1;
                    }
                    this.ChangeProgressDescription(description);
                    long lineStart = 0;
                    fileStream.Position = lineStart;
                    long piecesDone = 0;
                    Regex regex = null;
                    StringBuilder cache = new StringBuilder();
                    int bufferSize = 1024;
                    bool endReached = false;
                    byte[] bytes = null;
                    while (!endReached)
                    {
                        if (this.threadFileIndexer.CancellationPending)
                        {
                            Console.WriteLine("DEBUG: Indexer thread cancelled.");
                            this.StopProcess();
                            return;
                        }

                        bytes = new byte[bufferSize];

                        // Read a few bytes into buffer
                        int readBytes = fileStream.Read(bytes, 0, bufferSize);
                        if (readBytes < bufferSize)
                        {
                            endReached = true;
                        }
                        if (readBytes < 1)
                        {
                            break;
                        }
                        cache.Append(Encoding.Default.GetString(bytes));

                        // Check if any known log format matches the content
                        if (regex == null)
                        {
                            List<LogFormat> formats = PredefinedLogFormats.Formats;
                            List<int> matchingFormats = new List<int>();
                            for (int i = 0; i < formats.Count; i++)
                            {
                                LogFormat formatTest = formats[i];
                                string rexStrTest = formatTest.RegEx;

                                Regex regexTest = new Regex(rexStrTest, RegexOptions.Multiline);
                                bool checkTest = regexTest.IsMatch(cache.ToString());
                                if (checkTest)
                                {
                                    Console.WriteLine("Found matching LogFormat: " + formatTest.Name);
                                    matchingFormats.Add(i);
                                }
                            }

                            if (matchingFormats.Count < 1)
                            {
                                break;
                            }
                            if (matchingFormats.Count > 1)
                            {
                                Console.WriteLine("More than one format matches the input stream, chosing the first.");
                            }
                            LogFormat format = formats[matchingFormats[0]];
                            this.dataAccess.CurrentLogFormat = format;
                            logfile.Logformat = format;
                            string rexStr = format.RegEx;
                            regex = new Regex(rexStr, RegexOptions.Multiline);
                        }
                        if (regex == null)
                        {
                            continue;
                        }
                        bool check = regex.IsMatch(cache.ToString());
                        if (!check)
                        {
                            continue;
                        }
                        MatchCollection matchCol = regex.Matches(cache.ToString());
                        Console.WriteLine("Found matches: " + matchCol.Count);
                        int lastMatchPos = 0;
                        for (int i = 0; i < matchCol.Count; i++)
                        {
                            Match match = matchCol[i];
                            GroupCollection groups = match.Groups;
                            string type = groups["Type"].Value;
                            if (type == null)
                            {
                                Console.WriteLine("Type cannot be recognized.");
                                continue;
                            }
                            string date = groups["Date"].Value;
                            string time = groups["Time"].Value;
                            DateTime dateTime = DateTime.ParseExact(date + time, "dd.MM.yyyyHH:mm:ss", null);
                            if (dateTime < this.dataAccess.DateFirstEntry)
                            {
                                this.dataAccess.DateFirstEntry = dateTime;
                            }
                            if (dateTime > this.dataAccess.DateLastEntry)
                            {
                                this.dataAccess.DateLastEntry = dateTime;
                            }

                            // Add current log entry line number to list
                            long streamOffset = lineStart + match.Index;
                            MessageTypes currentType = MessageType.GetMessageType(type);
                            IndexObject iObj = new IndexObject();
                            iObj.FileIndex = streamOffset;
                            iObj.MessageType = currentType;
                            this.dataAccess.addIndex(iObj);

                            // Set next line start delimiter
                            lastMatchPos = match.Index + match.Length;
                        }
                        lineStart += lastMatchPos;

                        // Remove already found matches from buffer
                        cache.Remove(0, lastMatchPos);

                        if (fileStream.Position > (logfile.LastKnownPosition + piecesDone))
                        {
                            // Update Progress Bar
                            int percent = (int)((fileStream.Position * 100) / fileStream.Length);
                            if (percent > 100)
                            {
                                percent = 100;
                            }
                            this.threadFileIndexer.ReportProgress(percent, "Indexer");

                            //Console.WriteLine(">> " + fileStream.Position);
                            piecesDone += pieceSize;
                        }

                    } 

                    if (this.dataAccess.Indices.Count == 0)
                    {
                        string errorMsg = "Unable to recognize entries in log file.";
                        Console.WriteLine(errorMsg);
                        MessageBox.Show(errorMsg, "Read error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        this.StopProcess();
                        return;
                    }

                    // Save current position for watcher mode
                    logfile.LastKnownPosition = fileStream.Position;

                    TimeSpan timespan = new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);
                    Console.WriteLine("Finished after: " + timespan.ToString() + " HH:mm:ss");

                }
                logfile = this.dataAccess.FnProvider.GetNextFile();
            }

        }

        private void threadFileIndexer_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Update progressbar
            if (e.ProgressPercentage <= 100)
            {
                this.progressBar.Value = e.ProgressPercentage;
                this.lblPercent.Text = e.ProgressPercentage + " %";
            }

            // May be useful later
            //string progressType = (string)e.UserState;
        }

        private void threadFileIndexer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Sort and filter all entries
            this.dataAccess.SortListViewEntries();

            Console.WriteLine("Start time: " + this.dataAccess.DateFirstEntry.ToString());
            Console.WriteLine("End time:   " + this.dataAccess.DateLastEntry.ToString());

            this.DialogResult = DialogResult.OK;
            this.Close();
        }
    }
}